#region Using directives

/*
 * Copyright (c) 2007 Alan Dean.
 * All rights released.
 */

using System;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Security.Permissions;
using System.Xml;

using Edenbrook;

#endregion

namespace Edenbrook.Telephony
{
    #region Overview
    /// <summary>
    /// Represents a generic keypad.
    /// <para>This class cannot be inherited.</para>
    /// </summary>
    /// 
    /// <remarks>None</remarks>
    /// 
    /// <permission cref="T:System.Security.Permissions.StrongNameIdentityPermission">
    /// Callers must be signed with the Edenbrook strong name key.
    /// </permission>
    #endregion
    [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
    [StrongNameIdentityPermission(SecurityAction.Demand, PublicKey = PublicKey.Value)]
    public sealed class GenericKeypad : KeypadBase, IConfigurationSectionHandler
    {
        // public constructors

        #region public GenericKeypad()
        /// <overloads>
        /// Initializes a new instance of the <see cref="T:Edenbrook.Telephony.GenericKeypad"/> class.
        /// </overloads>
        /// 
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Edenbrook.Telephony.GenericKeypad"/> class.
        /// </summary>
        /// 
        /// <remarks>None</remarks>
        public GenericKeypad()
            : this(2, 10)
        { }
        #endregion

        #region public GenericKeypad(int, int)
        /// <summary>
        /// Initializes a new instance of the <see cref="T:Edenbrook.Telephony.GenericKeypad"/> class
        /// with the specified <paramref name="typingTime"/> and <paramref name="waitingTime"/>.
        /// </summary>
        /// 
        /// <param name="typingTime">The time taken to type a single character.</param>
        /// <param name="waitingTime">The time taken to wait for a character to 'fix'.</param>
        /// 
        /// <remarks>None</remarks>
        public GenericKeypad(int typingTime, int waitingTime)
            : base(typingTime, waitingTime)
        { }
        #endregion

        // public static methods

        #region public static GenericKeypad Configured()
        /// <summary>Returns the configured keypad.</summary>
        /// 
        /// <returns>The configured keypad.</returns>
        /// 
        /// <remarks>None</remarks>
        public static GenericKeypad Configured()
        {
            return Configured("edenbrook.keypad/generic");
        }
        #endregion

        #region public static GenericKeypad Configured(string)
        /// <summary>Returns the configured keypad.</summary>
        /// 
        /// <param name="sectionName">The configuration section path and name.</param>
        /// 
        /// <returns>The configured keypad.</returns>
        /// 
        /// <remarks>None</remarks>
        public static GenericKeypad Configured(string sectionName)
        {
            return (ConfigurationManager.GetSection(sectionName) as GenericKeypad);
        }
        #endregion

        // methods (IConfigurationSectionHandler)

        #region object IConfigurationSectionHandler.Create(object, object, XmlNode)
        /// <summary>Creates a configuration section handler.</summary>
        /// 
        /// <param name="parent">Parent object.</param>
        /// <param name="configContext">Configuration context object.</param>
        /// <param name="section">Section XML node.</param>
        /// 
        /// <returns>The created section handler object.</returns>
        /// 
        /// <remarks>None</remarks>
        object IConfigurationSectionHandler.Create(object parent, object configContext, XmlNode section)
        {
            GenericKeypad keypad = null;

            if (section != null)
            {
                XmlAttribute typingTime = section.Attributes["typingTime"];
                if (typingTime == null) throw new ConfigurationErrorsException("The <generic> element requires a typingTime attribute.");

                XmlAttribute waitingTime = section.Attributes["waitingTime"];
                if (waitingTime == null) throw new ConfigurationErrorsException("The <generic> element requires a waitingTime attribute.");

                keypad = new GenericKeypad(XmlConvert.ToInt32(typingTime.Value), XmlConvert.ToInt32(waitingTime.Value));

                foreach (XmlNode button in section.ChildNodes)
                {
                    if (button.NodeType == XmlNodeType.Element)
                    {
                        if (button.Name != "button") throw new ConfigurationErrorsException("<generic> may only contain <button> elements.");

                        XmlAttribute number = button.Attributes["number"];
                        if (number == null) throw new ConfigurationErrorsException("The <button> element requires a number attribute.");

                        int buttonNumber = XmlConvert.ToInt32(number.Value);

                        string text = button.InnerText;

                        if (String.IsNullOrEmpty(text))
                        {
                            // unused button
                        }
                        else
                        {
                            char[] chars = text.ToCharArray();

                            for (int i = 0; i < chars.Length; i++)
                            {
                                if (keypad.ContainsKey(chars[i])) throw new ConfigurationErrorsException(String.Concat("The '", chars[i], "' character is already defined on this keypad."));

                                keypad[chars[i]] = new CharacterLocation(buttonNumber, i);
                            }
                        }
                    }
                }
            }

            return keypad;
        }
        #endregion
    }
}